package gitbucket.core.view

import java.text.SimpleDateFormat
import java.util.Date
import javax.servlet.http.{HttpServletRequest, HttpSession}
import gitbucket.core.controller.Context
import gitbucket.core.model.Account
import gitbucket.core.service.RequestCache
import gitbucket.core.service.SystemSettingsService.{
  BasicBehavior,
  RepositoryOperation,
  RepositoryViewerSettings,
  Ssh,
  SystemSettings,
  Upload,
  WebHook
}
import org.mockito.Mockito._
import org.scalatest.funspec.AnyFunSpec
import play.twirl.api.Html

class AvatarImageProviderSpec extends AnyFunSpec {

  val request = mock(classOf[HttpServletRequest])
  val session = mock(classOf[HttpSession])
  when(request.getRequestURL).thenReturn(new StringBuffer("http://localhost:8080/path.html"))
  when(request.getRequestURI).thenReturn("/path.html")
  when(request.getContextPath).thenReturn("")
  when(request.getSession).thenReturn(session)

  describe("getAvatarImageHtml") {
    it("should show Gravatar image for no image account if gravatar integration is enabled") {
      implicit val context = Context(createSystemSettings(true), None, request)
      val provider = new AvatarImageProviderImpl(Some(createAccount(None)))

      assert(
        provider.toHtml("user", 32).toString ==
          """<img src="https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=32&d=retro&r=g" class="avatar" style="width: 32px; height: 32px;"
            |     alt="@user" />""".stripMargin
      )
    }

    it("should show uploaded image even if gravatar integration is enabled") {
      implicit val context = Context(createSystemSettings(true), None, request)
      val account = createAccount((Some("icon.png")))
      val date = new SimpleDateFormat("yyyyMMddHHmmss").format(account.updatedDate)
      val provider = new AvatarImageProviderImpl(Some(account))

      assert(
        provider.toHtml("user", 32).toString ==
          s"""<img src="/user/_avatar?${date}" class="avatar" style="width: 32px; height: 32px;"
             |     alt="@user" />""".stripMargin
      )
    }

    it("should show local image for no image account if gravatar integration is disabled") {
      implicit val context = Context(createSystemSettings(false), None, request)
      val account = createAccount(None)
      val date = new SimpleDateFormat("yyyyMMddHHmmss").format(account.updatedDate)
      val provider = new AvatarImageProviderImpl(Some(account))

      assert(
        provider.toHtml("user", 32).toString ==
          s"""<img src="/user/_avatar?${date}" class="avatar" style="width: 32px; height: 32px;"
             |     alt="@user" />""".stripMargin
      )
    }

    it("should show Gravatar image for specified mail address if gravatar integration is enabled") {
      implicit val context = Context(createSystemSettings(true), None, request)
      val provider = new AvatarImageProviderImpl(None)

      assert(
        provider.toHtml("user", 20, "hoge@hoge.com").toString ==
          """<img src="https://www.gravatar.com/avatar/4712f9b0e63f56ad952ad387eaa23b9c?s=20&d=retro&r=g" class="avatar-mini" style="width: 20px; height: 20px;"
            |     alt="@user" />""".stripMargin
      )
    }

    it("should show unknown image for unknown user if gravatar integration is enabled") {
      implicit val context = Context(createSystemSettings(true), None, request)
      val provider = new AvatarImageProviderImpl(None)

      assert(
        provider.toHtml("user", 20).toString ==
          """<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
            |     alt="@user" />""".stripMargin
      )
    }

    it("should show unknown image for specified mail address if gravatar integration is disabled") {
      implicit val context = Context(createSystemSettings(false), None, request)
      val provider = new AvatarImageProviderImpl(None)

      assert(
        provider.toHtml("user", 20, "hoge@hoge.com").toString ==
          """<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
            |     alt="@user" />""".stripMargin
      )
    }

    it("should add tooltip if it's enabled") {
      implicit val context = Context(createSystemSettings(false), None, request)
      val provider = new AvatarImageProviderImpl(None)

      assert(
        provider.toHtml("user", 20, "hoge@hoge.com", true).toString ==
          """<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
            |     alt="@user"
            |     data-toggle="tooltip" title="user" />""".stripMargin
      )
    }

    it("should escape user name") {
      implicit val context = Context(createSystemSettings(false), None, request)
      val provider = new AvatarImageProviderImpl(None)

      assert(
        provider.toHtml("""<user>"<name>""", 20, "hoge@hoge.com").toString ==
          """<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
            |     alt="@&lt;user&gt;&quot;&lt;name&gt;" />""".stripMargin
      )

      assert(
        provider.toHtml("""<user>"<name>""", 20, "hoge@hoge.com", true).toString ==
          """<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
            |     alt="@&lt;user&gt;&quot;&lt;name&gt;"
            |     data-toggle="tooltip" title="&lt;user&gt;&quot;&lt;name&gt;" />""".stripMargin
      )
    }
  }

  private def createAccount(image: Option[String]) =
    Account(
      userName = "user",
      fullName = "user@localhost",
      mailAddress = "",
      password = "",
      isAdmin = false,
      url = None,
      registeredDate = new Date(),
      updatedDate = new Date(),
      lastLoginDate = None,
      image = image,
      isGroupAccount = false,
      isRemoved = false,
      description = None
    )

  private def createSystemSettings(useGravatar: Boolean) =
    SystemSettings(
      baseUrl = None,
      information = None,
      basicBehavior = BasicBehavior(
        allowAccountRegistration = false,
        allowResetPassword = false,
        allowAnonymousAccess = true,
        isCreateRepoOptionPublic = true,
        repositoryOperation = RepositoryOperation(
          create = true,
          delete = true,
          rename = true,
          transfer = true,
          fork = true
        ),
        gravatar = useGravatar,
        notification = false,
        limitVisibleRepositories = false
      ),
      ssh = Ssh(
        enabled = false,
        bindAddress = None,
        publicAddress = None
      ),
      useSMTP = false,
      smtp = None,
      ldapAuthentication = false,
      ldap = None,
      oidcAuthentication = false,
      oidc = None,
      skinName = "skin-blue",
      userDefinedCss = None,
      showMailAddress = false,
      webHook = WebHook(
        blockPrivateAddress = false,
        whitelist = Nil
      ),
      upload = Upload(
        maxFileSize = 3 * 1024 * 1024,
        timeout = 30 * 10000,
        largeMaxFileSize = 3 * 1024 * 1024,
        largeTimeout = 30 * 10000
      ),
      repositoryViewer = RepositoryViewerSettings(
        maxFiles = 0,
        maxDiffFiles = 100,
        maxDiffLines = 1000
      ),
      "main"
    )

  /**
   * Adapter to test AvatarImageProviderImpl.
   */
  class AvatarImageProviderImpl(account: Option[Account]) extends AvatarImageProvider with RequestCache {

    def toHtml(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)(implicit
      context: Context
    ): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip)

    override def getAccountByMailAddressFromCache(mailAddress: String)(implicit context: Context): Option[Account] =
      account
    override def getAccountByUserNameFromCache(userName: String)(implicit context: Context): Option[Account] = account
  }

}
